require("SOD/StructureGenerator/StructureGenerator")

module SOD
  
  OPT_STATUS_STOPED   = -1
  OPT_STATUS_RUNNING  = 0
  OPT_STATUS_FINISHED = 1

  class StructureGeneratorData
    
    attr_accessor :bars, :slt_bars, :default_bar, :active_bar, :my_database_path, :dialog, :cppBar
    
    def initialize
      #structure_generator_dir = Plugin.dir + "/StructureGenerator"
      structure_generator_dir = Sketchup.find_support_file "StructureGenerator", "Plugins/SOD"
      @cppBar = StructureGenerator::BuildingBar.new(structure_generator_dir)
      

      init_default_bar
      @active_bar = new_default_bar
      init_bars
    end


    def new_default_bar
      @default_bar.dup
    end

    def init_bars
      @bars = Array.new
      @slt_bars = Array.new
    end

    def update_bar_by_model( rb_bar = @active_bar )
      rb_bar[:fp_wdt] = Plugin.model.sketchInfoContainer.footprintWidth.to_m.to_f
      rb_bar[:fp_lng] = Plugin.model.sketchInfoContainer.footprintLength.to_m.to_f
      rb_bar[:fx_hgh] = Plugin.model.projectInfo.minConstructionHeight #? \
        # Plugin.model.projectInfo.minConstructionHeight.to_mm.to_i : 300# flexibility height
      rb_bar[:n_stys] = Plugin.model.projectInfo.numberOfStoreys # number of storeys
      # rb_bar[:clzn_r] = []
      puts "rb_bar[:fp_wdt] - " + rb_bar[:fp_wdt].to_s
      puts "rb_bar[:fp_lng] - " + rb_bar[:fp_lng].to_s
    end
    
    def update_selected_by_hash(h)
      return nil if !(h.is_a? Hash)
      h.each {|key, value| 
        @active_bar[key] = value
        @slt_bars.each{ |idx| 
          @bars[idx][key] = value
        }    
      }
    end

    def update_selected_by_value(key,value)
      return if !@active_bar.has_key? key   
      @active_bar[key] = value
      @slt_bars.each{ |idx| 
          return if !@bars[idx].has_key? key   
          @bars[idx][key] = value
      }    
    end

    def add_bar(h)
      return nil if !(h.is_a? Hash)
      bar = new_default_bar
      h.each {|key, value| 
        return if !bar.has_key? key
        bar[key] = value
      }
      @bars.push(bar)
    end

    def select(idx)
      if idx.is_a? Integer
        return if @slt_bars.include? idx
        @slt_bars.push idx
      elsif idx.is_a? String
        sel = nil
        idx = idx.to_sym
        @bars.each_index{ |h| 
          sel = h if idx == @bars[h][:bar_id] 
        }
        return if @slt_bars.include? sel
        @slt_bars.push sel
      elsif idx.is_a? Symbol
        sel = nil
        @bars.each_index{ |h| 
          sel = h if idx == @bars[h][:bar_id] 
        }
        return if @slt_bars.include? sel
        @slt_bars.push sel
      else
        nil
      end
    end

    def init_default_bar 
      @default_bar = Hash.new
      @default_bar[:bar_id] = :NO_ID
      @default_bar[:fp_lng] = 0.0 # Plugin.model.sketchInfoContainer.footprintWidth.to_m.to_f # length of the footprint
      @default_bar[:fp_wdt] = 0.0 # Plugin.model.sketchInfoContainer.footprintLength.to_m.to_f # width of the footprint
      @default_bar[:cl_sps] = [] # possible column spacing
      @default_bar[:co_hgh] = 600 # construction height
      @default_bar[:fx_hgh] = Plugin.model.projectInfo.minConstructionHeight #? \
        # Plugin.model.projectInfo.minConstructionHeight.to_mm.to_i : 300# flexibility height
      @default_bar[:n_stys] = Plugin.model.projectInfo.numberOfStoreys # number of storeys
      @default_bar[:n_rows] = [1] # number of rows
      @default_bar[:dircts] = [0,1] # possible primary beam directions
      @default_bar[:fd_wdt] = [1.2] # facade width
      @default_bar[:p_muta] = 0.05 # mutation probability
      @default_bar[:p_crss] = 0.95 # corssover probability
      @default_bar[:p_size] = 500 # population size
      @default_bar[:nof_gn] = 1200 # nof generations
      @default_bar[:fitnss] = 0.0# fitness
      # clzn_0 = Plugin.model.sketchInfoContainer.footprintWidth.to_m.to_f * 0.4
      # clzn_1 = Plugin.model.sketchInfoContainer.footprintWidth.to_m.to_f * 0.4
      @default_bar[:clzn_r] = [0.0, 0.0]# column zone range low and heigh bound
      @default_bar[:clsp_r] = [3.0,10.0] # column spacing range low and heigh bound

      #results
      @default_bar[:db_rdy] = false# database ready
      @default_bar[:opt_st] = OPT_STATUS_STOPED # opt_status
      @default_bar[:ps_col] = 0.0# position of inner columns
      @default_bar[:gn_str] = [] # gene string
      @default_bar[:bm_hgh] = -1.0
      @default_bar[:sl_hgh] = -1.0

      @default_bar.each{ |k,v| v.freeze }
      @default_bar.freeze
    end

    def fitness ( rb_bar = @active_bar)
      rb_bar[:fitnss]
    end
    def position_of_inner_columns ( rb_bar = @active_bar)
      rb_bar[:ps_col]
    end
    def genes_string ( rb_bar = @active_bar)
      rb_bar[:gn_str]
    end
    def opt_status ( rb_bar = @active_bar)
      rb_bar[:opt_st]
    end

    def read_database( rb_bar = @active_bar)
      path = Plugin.dir + "/StructureGenerator/"
      @my_database_path = UI.openpanel("Read Database", path, ".txt")
      if @my_database_path.nil?
        puts "rb_bar[:db_rdy] - no path returned!" #Fabian
        return
      else
        puts "my_database_path:"
        puts @my_database_path.to_s
        rb_bar[:db_rdy] = @cppBar.ReadDateBase(@my_database_path.to_s) 
        puts "rb_bar[:db_rdy] - " + rb_bar[:db_rdy].to_s
      end
    end
    
    def run_generator( rb_bar = @active_bar)

      
      puts "rb_bar[:db_rdy] - " + rb_bar[:db_rdy].to_s
      if !(rb_bar[:db_rdy])
        puts "database not ready"
            osg_message = "<h3>database not ready</h3>"
          if dialog 
            dialog.updata_sg_message( osg_message )
          end
        return
      end

      update_bar_by_model

      return if ( rb_bar[:fp_lng] == 0 and rb_bar[:fp_wdt]  = 0 )
      
        puts "-------start run gengerator -----------"
        puts "Start at #{Time.now}"
      
        l = (rb_bar[:fp_lng]*1000).round.to_i;
        w = (rb_bar[:fp_wdt]*1000).round.to_i;    
        construtHeight = (rb_bar[:co_hgh]).round.to_i;  
        # nof_storeys = rb_bar[:n_stys]
        puts "l:" + l.to_s;
        puts "w:" + w.to_s;

        l_int = Integer(l)
        w_int = Integer(w)
        puts "l_int:" + l_int.to_s;
        puts "w_int:" + w_int.to_s;

        @cppBar.Length(l_int)
        @cppBar.Width(w_int)

        Plugin.model.projectInfo.getConstruction
        storey_hight = Plugin.model.projectInfo.minConstructionHeight.to_mm.to_i \
        + Plugin.model.projectInfo.minRoomHeight.to_mm.to_i \
        + construtHeight
        @cppBar.FloorHeight(Integer(storey_hight/10.0));


        
        puts " ------ my_bar -------"
        @cppBar.ConstrutHeight( construtHeight)
        @cppBar.No_of_stories(  rb_bar[:n_stys])
        @cppBar.PopulationSize( rb_bar[:p_size]);
        @cppBar.NofGenerations( rb_bar[:nof_gn]);
        @cppBar.MutationProbability(  rb_bar[:p_muta] );
        @cppBar.CrossoverProbability( rb_bar[:p_crss] );

        @cppBar.X_COL( rb_bar[:clzn_r][0].to_f );
        @cppBar.Y_COL( rb_bar[:clzn_r][1].to_f );
        
        cpp_l = @cppBar.Length()
        cpp_w = @cppBar.Width()
        puts cpp_l
        puts cpp_w
        puts @cppBar.FloorHeight().to_s
        puts @cppBar.ConstrutHeight().to_s
        puts @cppBar.No_of_stories().to_s
        puts @cppBar.PopulationSize().to_s
        puts @cppBar.NofGenerations().to_s
        puts @cppBar.MutationProbability().to_s
        puts @cppBar.CrossoverProbability().to_s
        puts @cppBar.X_COL().to_s
        puts @cppBar.Y_COL().to_s

        cl_sps_arr = Array(rb_bar[:cl_sps])
        if cl_sps_arr.empty? == 0
          dialog.updata_sg_message( "<h3>No Possible Spacing</h3>" ) if dialog
          return 
        end
        puts "Array(rb_bar[:cl_sps]).length: " + cl_sps_arr.length.to_s
        puts "Array(rb_bar[:cl_sps]): " + cl_sps_arr.to_s
        cpp_clsps_vec = StructureGenerator::IntVector.new( cl_sps_arr.length);
        0.upto(cpp_clsps_vec.size-1) { |i| cpp_clsps_vec[i] = cl_sps_arr[i] }
        @cppBar.PossibleSpacing(cpp_clsps_vec)
        
        print 'cl_sps_vec: '
        0.upto(cpp_clsps_vec.size-1) { |i| print cpp_clsps_vec[i],"; " }
        print "\n"
        
        puts "cl_sps_vec.length:"
        puts cpp_clsps_vec.size
        if cpp_clsps_vec.size == 0
          dialog.updata_sg_message( "<h3>No Possible Spacing</h3>" ) if dialog
          return 
        end

        dircts_arr = Array(rb_bar[:dircts])
        if dircts_arr.empty? == 0
          dialog.updata_sg_message( "<h3>No Possible Direction</h3>" ) if dialog
          return 
        end

        puts "Array(rb_bar[:dircts]).length: " + dircts_arr.length.to_s
        puts "Array(rb_bar[:dircts]): " + dircts_arr.to_s
        cpp_dircts_vec = StructureGenerator::IntVector.new( dircts_arr.length);
        0.upto(cpp_dircts_vec.size-1) { |i| cpp_dircts_vec[i] = dircts_arr[i] }
        @cppBar.PossibleDirections(cpp_dircts_vec)
        
        print 'cl_sps_vec: '
        0.upto(cpp_dircts_vec.size-1) { |i| print cpp_dircts_vec[i],"; " }
        print "\n"
        
        puts "cl_sps_vec.length:"
        puts cpp_dircts_vec.size
        if cpp_dircts_vec.size == 0
          dialog.updata_sg_message( "<h3>No Possible Direction</h3>" ) if dialog
          return 
        end






        
        puts @cppBar.Length()
        puts @cppBar.Width()
        puts @cppBar.ConstrutHeight()
        puts @cppBar.No_of_stories()

        puts @cppBar.CurrentDirectory()

        osg_message = "<h3>Running...</h3>"
        if dialog 
          dialog.updata_sg_message( osg_message )
        end

        rb_bar[:opt_st] = OPT_STATUS_RUNNING
        @cppBar.OptStatus(OPT_STATUS_RUNNING)

        @cppBar.RunGenerator()

        thread = Thread.new{
          sleep(0.1)
          run_time = 0.0
          time_step = 0.04
          id = 0;
          id_check = (0.1/0.04).to_i 
          puts "id_check"
          puts id_check
          while run_time < 30.0
            run_time += time_step
            id += 1
            sleep(0.04)
            puts id
            break if OPT_STATUS_FINISHED == @cppBar.OptStatus()
            # # if id%id_check == 0
            #   puts id/id_check 
            #   js_command = "passFromRubyToJsUpdateTimeIconById(\""+ id/id_check + "\")"
            #   puts js_command
            #   $g_my_dialog.execute_script(js_command)
            # # end
          end

          puts "End at #{Time.now}"

          rb_bar[:opt_st] = @cppBar.OptStatus()
          rb_bar[:gn_str] = @cppBar.Genes_string()
          
          rb_bar[:fitnss] = @cppBar.Fitness()
          rb_bar[:ps_col] = rb_bar[:clzn_r][0].to_f + rb_bar[:gn_str][0].to_i * @cppBar.Step_COL().to_f

          # puts "@cppBar.IsOptFinished()"
          puts "g_opt_status #{rb_bar[:opt_st]}"
          puts (rb_bar[:gn_str].map { |s| s.to_i }).join(",")
          puts rb_bar[:fitnss]
          puts rb_bar[:ps_col]


          fit  = rb_bar[:fitnss]
          gArr = rb_bar[:gn_str]
          pstn = rb_bar[:ps_col]

          bm_hgh_2nd = Plugin.sgdata.cppBar.SectionHeight( Integer(rb_bar[:gn_str][4]) )
          bm_hgh_1st = Plugin.sgdata.cppBar.SectionHeight( Integer(rb_bar[:gn_str][6]) )
          rb_bar[:bm_hgh] = bm_hgh_2nd > bm_hgh_1st ? bm_hgh_2nd : bm_hgh_1st
          puts "rb_bar[:bm_hgh]: " + rb_bar[:bm_hgh].to_s
          rb_bar[:sl_hgh] = rb_bar[:gn_str][3]
          puts "rb_bar[:sl_hgh]: " + rb_bar[:sl_hgh].to_s

          osg_message = ""
          if @cppBar.HasSolution() 

            osg_message = "<h3>Done :&nbsp;&nbsp;" + format("%.2f", fit.to_f) + "</h3><br/>"
            osg_message += "<table><tbody>"
            osg_message += "<tr><td>Inner Column Position:</td>"
            osg_message += "<td>"+format("%.1f", pstn.to_f) + "</td></tr>"
            osg_message += "<tr><td>Number of Column in Row:"
            osg_message += "<td>"+(2 + gArr[1].to_i).to_s  + "</td></tr>"
            if 0 == gArr[8]
            osg_message += "<tr><td>Primary Beams' Direction:</td><td>transverse</td>"
            osg_message += "<tr><td>Number of Secondary Beams in Span:</td><td>"+ (gArr[10].to_i).to_s  + "</td></tr>"
            osg_message += "<tr><td>Width of Secondary Beam:</td><td>"+ format("%.2f", (( rb_bar[:fp_wdt].to_f)/(gArr[10].to_i-1).to_f))  + "</td></tr>"
            # puts format("%.2f", (( w_int.to_f)/(gArr[10].to_i-1).to_f))
            else
            osg_message += "<tr><td>Primary Beams' Direction:</td><td>longitudinal</td>"
            osg_message += "<tr><td>Number of Secondary Beams in Span:</td><td>"+ (gArr[10].to_i).to_s  + "</td></tr>"
            osg_message += "<tr><td>Width of Secondary Beam:</td><td>"+ format("%.2f", (( rb_bar[:fp_lng].to_f)/(gArr[10].to_i-1).to_f))  + "</td></tr>"
            end
            osg_message += "<tr><td>2nd:&nbsp;&nbsp;" + @cppBar.SectionName(gArr[4].to_i).to_s 
            osg_message += "&nbsp;-&nbsp;S"+gArr[5].to_i.to_s + "</td><td>"
            osg_message += "1st:&nbsp;&nbsp;" + @cppBar.SectionName(gArr[6].to_i).to_s 
            osg_message += "&nbsp;-&nbsp;S"+gArr[7].to_i.to_s + "</td></tr>"
            i = 0
            while i < rb_bar[:n_stys].to_i  do
              osg_message += "<tr><td>(&nbsp;" +@cppBar.SectionName(gArr[11+i*4]) 
              osg_message += "&nbsp;-&nbsp;S"+gArr[12+i*4].to_i.to_s + ")</td><td>";
              osg_message += "(&nbsp;" +@cppBar.SectionName(gArr[13+i*4]) 
              osg_message += "&nbsp;-&nbsp;S"+gArr[14+i*4].to_i.to_s + "&nbsp;)</td></tr>";
              i +=1;
            end
            osg_message += "</tbody></table>"
            osg_message += "</p>"
          else
            osg_message = "<h3>no solution<h3><br/>"
          end
          dialog.updata_sg_message( osg_message ) if dialog
          
        }

        Sketchup.send_action("Run_Structure_Generator:")

    end
    
  end
  
end
